﻿
#region USING

using System;
using System.Collections.Generic;
using System.Linq;

#endregion

namespace Learning.ConwaysGameOfLife
{
    public class SquareGrid
    {
		#region Fields (1) 

        private List<Cell> _cells;

		#endregion Fields 

		#region Constructors (1) 

        public SquareGrid(int size, IList<Cordinate> intialAliveCells)
        {
            this.Size = size;
            this.CreateGrid();
            this.SetIntialAliveCellsForCurrentCells(intialAliveCells);
            this.InitializeNextCellsState();
        }

		#endregion Constructors 

		#region Properties (4) 

        public List<Cell> AliveCells
        {
            get
            {
                return this.Cells
                    .Where<Cell>(cell => cell.State == CellState.Alive)
                    .ToList<Cell>();
            }
        }

         public List<Cell> Cells
        {
            get
            {
                if (this._cells == null)
                {
                    this._cells = new List<Cell>();
                }

                return this._cells;
            }
        }

        private List<Cell> NextCellsState { get; set; }

        public int Size { get; set; }

		#endregion Properties 

		#region Methods (17) 

		// Public Methods (6) 

        public void ChangeCellsStateToAlive(IList<Cordinate> aliveCells)
        {
            foreach (Cordinate cordinate in aliveCells)
            {
                this.SetNextCellState(cordinate, CellState.Alive);
            }
        }

        public Cell FindCell(Cordinate cordinate)
        {
            return this.Cells.FindCell(cordinate);
        }

        public IList<Cell> FindNeighbors(Cordinate cordinate)
        {
            List<Cell> neighbors = new List<Cell>();

            FindUpperLeftNeighbor(cordinate, neighbors);
            FindDirectlyAboveNeighbor(cordinate, neighbors);
            FindUpperRightNeighbor(cordinate, neighbors);
            FindLeftNeighbor(cordinate, neighbors);
            FindRightNeighbor(cordinate, neighbors);
            FindLowerLeftNeighbor(cordinate, neighbors);
            FindDirectlyBelowNeighbor(cordinate, neighbors);
            FindLowerRightNeighbor(cordinate, neighbors);

            return neighbors;    
        }

        public CordinateEnumerator GetCordinates()
        {
            return CordinateEnumerator.CreateCordinateEnumeratorForSize(this.Size);
        }

        public CellState GetNextCellState(Cordinate cordinate)
        {
            return this.NextCellsState.FindCell(cordinate).State;
        }

        public void SetNextCellState(Cordinate cordinate, CellState newState)
        {
            Cell cell = this.NextCellsState.FindCell(cordinate);

            cell.State = newState;
        }
		// Private Methods (11) 

        private void CreateGrid()
        {
            foreach (Cordinate cordinate in CordinateEnumerator.CreateCordinateEnumeratorForSize(this.Size))
            {
                this.Cells.Add(new Cell(cordinate, CellState.Dead));
            }
        }

        private void FindDirectlyAboveNeighbor(Cordinate cordinate, List<Cell> neighbors)
        {
            neighbors.AddNeighborCell(this.Cells, cordinate.MoveCordinate(CordinateMoveDirection.Previous, CordinateMoveDirection.None));
        }

        private void FindDirectlyBelowNeighbor(Cordinate cordinate, List<Cell> neighbors)
        {
            neighbors.AddNeighborCell(this.Cells, cordinate.MoveCordinate(CordinateMoveDirection.Next, CordinateMoveDirection.None));
        }

        private void FindLeftNeighbor(Cordinate cordinate, List<Cell> neighbors)
        {
            neighbors.AddNeighborCell(this.Cells, cordinate.MoveCordinate(CordinateMoveDirection.None, CordinateMoveDirection.Previous));
        }

        private void FindLowerLeftNeighbor(Cordinate cordinate, List<Cell> neighbors)
        {
            neighbors.AddNeighborCell(this.Cells, cordinate.MoveCordinate(CordinateMoveDirection.Next, CordinateMoveDirection.Previous));
        }

        private void FindLowerRightNeighbor(Cordinate cordinate, List<Cell> neighbors)
        {
            neighbors.AddNeighborCell(this.Cells, cordinate.MoveCordinate(CordinateMoveDirection.Next, CordinateMoveDirection.Next));
        }

        private void FindRightNeighbor(Cordinate cordinate, List<Cell> neighbors)
        {
            neighbors.AddNeighborCell(this.Cells, cordinate.MoveCordinate(CordinateMoveDirection.None, CordinateMoveDirection.Next));
        }

        private void FindUpperLeftNeighbor(Cordinate cordinate, List<Cell> neighbors)
        {
            neighbors.AddNeighborCell(this.Cells, cordinate.MoveCordinate(CordinateMoveDirection.Previous, CordinateMoveDirection.Previous));
        }

        private void FindUpperRightNeighbor(Cordinate cordinate, List<Cell> neighbors)
        {
            neighbors.AddNeighborCell(this.Cells, cordinate.MoveCordinate(CordinateMoveDirection.Previous, CordinateMoveDirection.Next));
        }

        private void InitializeNextCellsState()
        {
            this.NextCellsState = new List<Cell>(this.Cells.Count);

            this.Cells.ForEach((item) =>
                {
                    this.NextCellsState.Add(item.Copy());
                });
        }

        private void SetIntialAliveCellsForCurrentCells(IList<Cordinate> initialAliveCellList)
        {
            if (initialAliveCellList == null)
            {
                return;
            }

            foreach (var cellLocation in initialAliveCellList)
            {
                this.Cells.FindCell(cellLocation).State = CellState.Alive;
            }
        }

		#endregion Methods 
    }
}